local snow = require("meet_your_mods.mhr").snow

local routine_framework = {
    enum = {
        pre_hook       = 1,
        post_hook      = 2,
        before_village = 4,
        return_village = 8,
        return_title   = 16,
        all            = 31,
    },
    hooks = {
        {
            method = "initializeBeforeVillage",
            symble = "before_village",
            pre = {},
            post = {},
        },
        {
            method = "notifyReturnInVillage",
            symble = "return_village",
            pre = {},
            post = {},
        },
        {
            method = "titleReset",
            symble = "return_title",
            pre = {},
            post = {},
        },
    },

    pre_return_village  = true,
    post_return_village = true,
}

function routine_framework:hook(component, priority, target, hook)
    for _, hook in pairs(self.hooks) do
        for _, prefix in pairs({ "pre", "post", }) do
            local delegate = rawget(component, prefix .. "_" .. hook.symble)
            if not delegate then
                goto next_delegate
            end

            local hooks = rawget(hook, prefix)
            table.insert(hooks, priority or #hooks + 1, {
                component = component,
                method    = delegate
            })
            ::next_delegate::
        end
    end
end

function routine_framework:_run_pre_hook_(hooks, parameters)
    local return_value = sdk.PreHookResult.CALL_ORIGINAL
    for _, hook in pairs(hooks) do
        return_value = sdk.PreHookResult.SKIP_ORIGINAL == hook.method(hook.component, parameters)
            and sdk.PreHookResult.SKIP_ORIGINAL
            or sdk.PreHookResult.CALL_ORIGINAL
    end
    return return_value
end

function routine_framework:_run_post_hook_(hooks, result)
    local return_value = nil
    for _, hook in pairs(hooks) do
        return_value = hook.method(hook.component, result) or result
    end
    return return_value or result
end

function routine_framework:pre_initializeBeforeVillage(parameters)
    self.pre_return_village  = false
    self.post_return_village = false
    return self:_run_pre_hook_(self.hooks[1].pre, parameters)
end

function routine_framework:post_initializeBeforeVillage(result)
    return self:_run_pre_hook_(self.hooks[1].post, result)
end

function routine_framework:pre_notifyReturnInVillage(parameters)
    if self.pre_return_village then
        return sdk.PreHookResult.CALL_ORIGINAL
    end
    self.pre_return_village = true

    return self:_run_pre_hook_(self.hooks[2].pre, parameters)
end

function routine_framework:post_notifyReturnInVillage(result)
    if self.post_return_village then
        return result
    end
    self.post_return_village = true

    return self:_run_post_hook_(self.hooks[2].post, result)
end

function routine_framework:pre_titleReset(parameters)
    return self:_run_pre_hook_(self.hooks[3].pre, parameters)
end

function routine_framework:post_titleReset(result)
    return self:_run_post_hook_(self.hooks[3].post, result)
end

function routine_framework:initialize()
    snow.data.DataManager.initializeBeforeVillage:sdk_auto_hook(self)
    snow.gui.GuiManager.notifyReturnInVillage:sdk_auto_hook(self)
    snow.data.DataManager.titleReset:sdk_auto_hook(self)
end

return routine_framework
